﻿using System;
using System.Collections.Generic;
using System.Linq;




namespace myLittleC
{
    enum AsmMnem
    {
        Add,
        And,
        Call,
        Cdq,
        Cld,
        Cmp,
        Dec,
        Div,
        End,
        Fadd,
        Fxch,
        Fchs,
        Fcomip,
        Fdiv,
        Ffree,
        Fild,
        Fisttp,
        Fld,
        Fldz,
        Fmul,
        Fstp,
        Fsub,
        Je,
        Jmp,
        Jne,
        Jz,
        Jnz,
        Idiv,
        Inc,
        Lea,
        Loop,
        Label,
        Mov,
        Movsd,
        Movzx,
        Mul,
        Neg,
        Nop,
        Not,
        Or,
        Pop,
        RepMovsd,
        Ret,
        Push,
        Seta,
        Setae,
        Setb,
        Setbe,
        Sete,
        Setg,
        Setge,
        Setl,
        Setle,
        Setne,
        Setz,
        Shl,
        Shr,
        Std,
        Sub,
        Test,
        Xor,
    };

    #region Операнды

    class AsmOperand
    {
        protected readonly object Value;

        public virtual bool IsLink { get { return false; } }
        public virtual bool IsInt { get { return false; } }
        public virtual bool IsReg { get { return false; } }

        public String Op { get { return Value as String; } }
        public Int64 IntOp { get { return (long)Value; } }

        public bool UseReg(AsmRegOperand iReg) { return this == iReg || (IsLink && Value == iReg); }
        
       // public static bool operator ==(AsmOperand a, AsmOperand b) { return a.Value == b.Value; }
      //  public static bool operator !=(AsmOperand a, AsmOperand b) { return !(a == b); }

        public bool Equals(AsmOperand b)    {   return b != null && Value == b.Value;}
        public bool NotEquals(AsmOperand b) {   return !Equals(b);}

        public bool Alike(AsmOperand b)
        {
            if (b == null)
                return false;
            if (Equals(b))
                return true;
            if (IsLink && Value is AsmRegOperand && b.IsReg && Value == b)
            {
                return true; 
            }
            return  b.IsLink && b.Value is AsmRegOperand && IsReg && b.Value == this;
        }

        public AsmOperand(Object iOp) { Value = iOp; }
        public override string ToString() { return String.Format("{0}", Value); }
    }

    class AsmRegOperand : AsmOperand
    {
        public String RegName { get { return Op; } }
        public override bool IsReg { get { return true; } }
        public AsmRegOperand(String iOp) : base(iOp) { }
        public override string ToString() { return RegName; }
    }

    class AsmIntOperand : AsmOperand
    {
        public new Int64 Value { get { return IntOp; } }
        public override bool IsInt { get { return true; } }
        public AsmIntOperand(Int64 iOp) : base(iOp) { }
        public override string ToString() { return String.Format("dword ptr {0}", Value); }
    }

    class AsmLinkOperand : AsmOperand
    {
        private readonly Int64 _shift;

        public bool IsGlobal { get; set; }
        public override bool IsLink { get { return true; } }

        public AsmLinkOperand(String iOp) : this(iOp, false) { }
        public AsmLinkOperand(AsmOperand iOp) : this(iOp, false) { }

        public AsmLinkOperand(AsmOperand iOp, Int64 iShift) : this(iOp, false) { _shift = iShift; }
        public AsmLinkOperand(String iOp, bool global) : base(iOp) { IsGlobal = global; }
        public AsmLinkOperand(AsmOperand iOp, bool global) : base(iOp) { IsGlobal = global; }

        public new AsmOperand Value
        { get { return base.Value is AsmOperand ? base.Value as AsmOperand : new AsmOperand(Op); } }

        public override string ToString()
        {
            return String.Format(IsGlobal ? "offset {0}"
                : (_shift == 0) ? "dword ptr [{0}]" : "dword ptr [{0} + {1}]", Value, _shift);
        }
    }
    #endregion

    /// <summary>
    /// Просто общий класс
    /// </summary>
    abstract class AsmCode { };

    #region Классы данных
    class AsmVar : AsmCode
    {
        public String Name { get; private set; }
        public SyntaxExpression Value { get; private set; }

        public virtual String GenName { get { return String.Format("G$_{0}", Name); } }

        protected AsmVar(String iName, SyntaxExpression iValue = null)
        {
            Name = iName;
            if (iValue != null && !iValue.isConst)
                throw new ComException(ERRORS.GlobalVarIsNotConsExpr);
            Value = iValue;
        }

        public override string ToString()
        {
            return String.Format("    G$_{0} dd {1}", Name, Value != null ? Value.ToString() : "0");
        }
    }

    class AsmDd : AsmVar
    {
        public AsmDd(String iName, SyntaxExpression iValue = null) : base(iName, iValue) { }
    }

    class AsmDb : AsmVar
    {
        public AsmDb(String iName, SyntaxExpression iValue) : base(iName, iValue) { }

        public override string ToString()
        { return String.Format("    S$_{0} db \"{1}\",0", Name, Value != null ? Value.ToString() : "0"); }
    }

    class AsmArray : AsmDd
    {
        public UInt64 Size { get; private set; }
        public AsmArray(String iName, UInt64 iSize) : base(iName) { Size = iSize; }

        public override string ToString()
        { return String.Format("    G$_{0} dd {1} dup(0)", Name, Size); }
    }

    class AsmLocalVars : AsmVar
    {
        public override String GenName { get { return String.Format("L$_{0}", Name); } }

        public UInt64 Shift { get; private set; }
        public bool IsArg { get; private set; }

        public AsmLocalVars(String iName, UInt64 iShift, bool isArg = false)
            : base(iName)
        {
            Shift = iShift;
            IsArg = isArg;
        }

        public override string ToString() { return String.Format("    {0} equ ebp{2}{1}", GenName, Shift, IsArg ? "+" : "-"); }
    }
    #endregion

    class AsmCmd : AsmCode
    {
        public AsmCmd(AsmMnem iCmd, AsmOperand iOp1 = null, AsmOperand iOp2 = null)
        {
            Cmd = iCmd;
            AsmOp1 = iOp1;
            AsmOp2 = iOp2;
        }

        public AsmMnem Cmd { get; private set; }

        public AsmOperand AsmOp1 { get; private set; }
        public AsmOperand AsmOp2 { get; private set; }

        public bool IsOp1Int { get { return AsmOp1.IsInt; } }
        public bool IsOp2Int { get { return AsmOp2.IsInt; } }

        public override string ToString()
        {
            return AsmOp2 == null ? String.Format("    {0} {1}", Cmd, AsmOp1) : String.Format("    {0} {1}, {2}", Cmd, AsmOp1, AsmOp2);
        }

        public bool TouchStack
        {
            get{   return Cmd == AsmMnem.Push || Cmd == AsmMnem.Pop || AsmOp1.UseReg(AsmCodeGenerator.RegEsp) || Cmd == AsmMnem.Call;}
        }

        public bool Jump
        {
            get{   return Cmd == AsmMnem.Je || Cmd == AsmMnem.Jmp || Cmd == AsmMnem.Jne || Cmd == AsmMnem.Jz || Cmd == AsmMnem.Jnz;}
        }
    };

    class AsmLabel : AsmCmd //поидее должен быть AsmVar, но тогда его не вставить в в блок кода
    {
        public String Name { get; private set; }

        public AsmLabel(String iName) : base(AsmMnem.Label) { Name = iName; }

        public override string ToString() { return String.Format("{0}:", Name); }
    }

    class AsmFunck : AsmCode
    {
        public string Name { get; private set; }

        public UInt64 Shift { get; private set; }
        public Int64 Args { get; private set; }

        public readonly LinkedList<AsmLocalVars> Vars = new LinkedList<AsmLocalVars>();
        public LinkedList<AsmCmd> Codes = new LinkedList<AsmCmd>();

        public AsmFunck(String funcName) { Name = funcName; }

        public void AddCmd(AsmMnem cmd, AsmOperand op) { Codes.AddLast(new AsmCmd(cmd, op)); }
        public void AddCmd(AsmMnem cmd, AsmOperand op1, AsmOperand op2) { Codes.AddLast(new AsmCmd(cmd, op1, op2)); }

        public void AddStruct(SymVar iStruct) { Vars.AddLast(new AsmLocalVars(iStruct.Name, Shift += ((UInt64)iStruct.Type.Size))); }
        public void AddArg(String varName) { Vars.AddLast(new AsmLocalVars(varName, (UInt64)(8 + (Args++) * 4), true)); }
        public void AddVar(SymVar var) { Vars.AddLast(new AsmLocalVars(var.Name, Shift += ((UInt64)var.Type.Size))); }
        public void AddArray(SymVarArray array) { Vars.AddLast(new AsmLocalVars(array.Name, Shift += (array.SumLenght * (UInt64)(array.Type.Size)))); }

        public void AddLabel(AsmLabel label) { Codes.AddLast(label); }

        public void Optimise() { Codes = LowLevelOptimiser.Optimise(Codes); }

        public override string ToString()
        {
            var res = "F$_" + Name + ":\n";

            res = Vars.Cast<AsmCode>().Aggregate(res, (current, var) => current + (var.ToString() + '\n'));

            res += new AsmCmd(AsmMnem.Push, AsmCodeGenerator.RegEbp).ToString() + '\n';
            res += new AsmCmd(AsmMnem.Mov, AsmCodeGenerator.RegEbp, AsmCodeGenerator.RegEsp).ToString() + '\n';
            res += new AsmCmd(AsmMnem.Sub, AsmCodeGenerator.RegEsp, new AsmIntOperand((long)Shift)).ToString() + '\n';

            res = Codes.Cast<AsmCode>().Aggregate(res, (current, code) => current + (code.ToString() + '\n'));

            res += new AsmCmd(AsmMnem.Add, AsmCodeGenerator.RegEsp, new AsmIntOperand((long)Shift)).ToString() + '\n';
            res += new AsmCmd(AsmMnem.Pop, AsmCodeGenerator.RegEbp).ToString() + '\n';
            res += new AsmCmd(AsmMnem.Ret).ToString() + '\n';

            return res;
        }
    }

    class AsmCodeGenerator : ICodeGenerator
    {
        public static readonly AsmRegOperand RegEax = new AsmRegOperand("eax");
        public static readonly AsmRegOperand RegEbx = new AsmRegOperand("ebx");
        public static readonly AsmRegOperand RegEdx = new AsmRegOperand("edx");
        public static readonly AsmRegOperand RegEcx = new AsmRegOperand("ecx");
        public static readonly AsmRegOperand RegEsp = new AsmRegOperand("esp");
        public static readonly AsmRegOperand RegEbp = new AsmRegOperand("ebp");

        public static readonly AsmRegOperand RegCl = new AsmRegOperand("cl");

        public static readonly AsmRegOperand RegSt0 = new AsmRegOperand("st(0)");
        public static readonly AsmRegOperand RegSt1 = new AsmRegOperand("st(1)");

        public const int CommandSize = 4;
        public static readonly AsmIntOperand AsmOpComSize = new AsmIntOperand(CommandSize);

        private readonly Optimisers _opt;
        AsmFunck _useFunk;

        private int _labelsCount;

        ParserWithStatement Parser { get; set; }
        readonly LinkedList<AsmCode> _globalData = new LinkedList<AsmCode>();
        readonly LinkedList<AsmFunck> _functions = new LinkedList<AsmFunck>();

        public AsmCodeGenerator(ParserWithStatement iParser, Optimisers opt)
        {
            Parser = iParser;
            _opt = opt;
        }

        public void Generate()
        {
            if (!Parser.Global.HaveFunk("main"))
                throw new ComException(ERRORS.ExpMainFunk);

            foreach (var node in Parser.Global.Table.Vareables.OfType<SymVar>())
            {
                if (node.Type is SymTypeArray)
                    _globalData.AddLast(new AsmArray(node.Name, ((SymVarArray)node).SumLenght));
                else if (node.Type is SymStruct)
                    _globalData.AddLast(new AsmArray(node.Name, ((UInt64)node.Type.Size)));
                else
                    _globalData.AddLast(new AsmDd(node.Name, node.Value));
            }

            foreach (var asmCode in Parser.Global.Table.Funks)
            {
                if (asmCode.Name == "printf")
                    continue;
                _functions.AddLast(GenerateFunc(asmCode as SymFunc));
            }
        }

        #region Добавление Команд и пеменных

        private void AddCmd(AsmMnem cmd, AsmOperand op) { _useFunk.AddCmd(cmd, op); }
        private void AddCmd(AsmMnem cmd, AsmOperand op1, AsmOperand op2) { _useFunk.AddCmd(cmd, op1, op2); }

        private void AddStruct(SymVar iStruct) { _useFunk.AddStruct(iStruct); }
        private void AddArg(String varName) { _useFunk.AddArg(varName); }
        private void AddVar(SymVar var) { _useFunk.AddVar(var); }
        private void AddArray(SymVarArray array) { _useFunk.AddArray(array); }

        private void AddLabel(AsmLabel label) { _useFunk.AddLabel(label); }
        private AsmLabel CreteLabel() { return new AsmLabel(String.Format("L$_{0}", _labelsCount++)); }

        private static String VarName(SyntaxIdentefier iden) { return String.Format("{1}{0}", iden.Name, iden.IsGlobal ? "G$_" : "L$_"); }
        #endregion

        #region ГенерацияКода
        AsmFunck GenerateFunc(SymFunc iFunc)
        {
            _useFunk = new AsmFunck(iFunc.Name);
            GenerateLocalVars(iFunc.Body.Table.Vareables);

            GenerateAsmBlock(iFunc.Body);
            AddLabel(new AsmLabel("E$_" + iFunc.Name));

            if (_opt == Optimisers.LowLavel)
                _useFunk.Optimise();

            return _useFunk;
        }

        void GenerateLocalVars(IEnumerable<ISymbol> iVars)
        {
            foreach (SymVar var in iVars)
            {
                if (var.Type is SymTypeArray)
                    AddArray(var as SymVarArray);
                else if (var.Type is SymStruct)
                    AddStruct(var);
                else if (var.IsFuncArg)
                    AddArg(var.Name);
                else
                {
                    AddVar(var);

                    if (var.Value != null)
                    {
                        var t = new SyntaxBinaryOp(SubTokenType.Assignment, new SyntaxIdentefier(var.Name), var.Value, var.Type) { Left = { ExType = var.Type } };
                        GenerateStatement(t);
                        AddCmd(AsmMnem.Add, RegEsp, new AsmIntOperand(4));
                    }
                }
            }
        }

        void GenerateAsmBlock(SyntaxBlock exp)
        {
            foreach (var node in exp.SyntaxNodes)
            {
                if (node is SyntaxReturn)
                    GenerateReturn(node as SyntaxReturn);
                else if (!(node is SyntaxExpression))
                    GenerateAsmOperators(node as SyntaxOperators);
                else
                {
                    GenerateStatement(node as SyntaxExpression);
                    AddCmd(AsmMnem.Add, RegEsp, AsmOpComSize);
                }
            }
        }

        void GenerateStatement(SyntaxNode exp)
        {
            if (exp is SyntaxUnaryOp)
                GenerateUnExpr(exp as SyntaxUnaryOp);
            else if (exp is SyntaxDotOp)
            {
                GenerateDot(exp as SyntaxDotOp);
                AddCmd(AsmMnem.Push, new AsmLinkOperand(RegEax));
            }
            else if (exp is SyntaxBinaryOp)
            {
                if ((exp as SyntaxBinaryOp).Operation >= SubTokenType.Assignment &&
                    (exp as SyntaxBinaryOp).Operation <= SubTokenType.DivAssignment)
                    GenerateAssignment(exp as SyntaxBinaryOp);
                else
                    GenerateBinExpr(exp as SyntaxBinaryOp);
            }
            else if (exp is SyntaxIdentefier)
            {
                if (exp.ExType is SymTypeArray || exp.ExType is SymTypeLink)
                {
                    AddCmd(AsmMnem.Lea, RegEax, new AsmLinkOperand(VarName((exp as SyntaxIdentefier))));
                    AddCmd(AsmMnem.Push, RegEax);
                }
                else
                    AddCmd(AsmMnem.Push, new AsmLinkOperand(VarName((exp as SyntaxIdentefier))));
            }
            else if (exp is SyntaxConst)
            {
                AddCmd(AsmMnem.Push, new AsmIntOperand(Convert.ToInt64(exp.ToString())));
            }
            
            else if (exp is SyntaxIndexArray)
            {
                GenerateIndexOfArray(exp as SyntaxIndexArray);
                AddCmd(AsmMnem.Push, new AsmLinkOperand(RegEax));
            }
            else if (exp is SyntaxCallFunc)
                GenerateCallFunc(exp as SyntaxCallFunc);
            else if (exp is SyntaxOperators)
                GenerateAsmOperators(exp as SyntaxOperators);
            else
                throw new ComException(ERRORS.BadExpression);
        }

        void GetLink(SyntaxExpression exp)
        {
            if (exp is SyntaxIdentefier)
            {
                bool isGlobal = (exp as SyntaxIdentefier).IsGlobal;

                AddCmd((isGlobal) ? AsmMnem.Mov : AsmMnem.Lea, RegEax, new AsmLinkOperand(VarName(exp as SyntaxIdentefier), isGlobal));
            }
            else if (exp is SyntaxDotOp)
                GenerateDot(exp as SyntaxDotOp);
            else if (exp is SyntaxIndexArray)
                GenerateIndexOfArray(exp as SyntaxIndexArray);
        }

        static UInt64 GenerateShift(SyntaxDotOp exp)
        {
            UInt64 sh = 0;
            for (var i = 0; exp.StructType.Items.Vars[i].Name != exp.StructMemberName.Name; i++)
            {
                if (exp.StructType.Items.Vars[i].Name != exp.StructMemberName.Name)
                    sh += (UInt64)((SymVar)exp.StructType.Items.Vars[i]).Type.Size;
            }
            return sh;
        }

        void GenerateDot(SyntaxDotOp exp)
        {
            GetLink(exp.Struct);

            if (exp is SyntaxlArrowOp)
                AddCmd(AsmMnem.Mov, RegEax, new AsmLinkOperand(RegEax));

            var sh = GenerateShift(exp);

            if (exp.StructMember is SyntaxDotOp)
                sh += GenerateShift(exp.StructMember as SyntaxDotOp);

            AddCmd(AsmMnem.Add, RegEax, new AsmIntOperand((Int64)sh)); //TODO: разобраться с типами
        }

        void GenerateIndexOfArray(SyntaxIndexArray exp)
        {
            if (!(exp.ArrayName.ExType is SymTypeArray))
            {
                GenerateStatement(exp.Indexes[0]);

                AddCmd(AsmMnem.Pop, RegEax);
                AddCmd(AsmMnem.Mov, RegEcx, new AsmIntOperand((4)));
                AddCmd(AsmMnem.Mul, RegEcx);
                AddCmd(AsmMnem.Push, RegEax);

                GetLink(exp.ArrayName);

                AddCmd(AsmMnem.Add, RegEax, new AsmIntOperand((12)));
                AddCmd(AsmMnem.Pop, RegEbx);

                AddCmd(AsmMnem.Add, RegEax, RegEbx);
            }
            else
            {
                var arrSize = ((SymTypeArray)exp.ArrayName.ExType).SumLenght;
                GetLink(exp.ArrayName);

                for (var i = 0; i < exp.Indexes.Count; i++)
                {
                    arrSize /= ((SymTypeArray)exp.ArrayName.ExType).Lenght[i];

                    AddCmd(AsmMnem.Push, RegEax);

                    GenerateStatement(exp.Indexes[i]);

                    AddCmd(AsmMnem.Pop, RegEax);
                    AddCmd(AsmMnem.Mov, RegEcx, new AsmIntOperand((Int64)(arrSize * 4)));
                    AddCmd(AsmMnem.Mul, RegEcx);
                    AddCmd(AsmMnem.Add, new AsmLinkOperand(RegEsp), RegEax);
                    AddCmd(AsmMnem.Pop, RegEax);
                }
            }
        }


        void LoadFloatVal(SyntaxExpression exp)
        {
            AddCmd((exp.ExType is SymTypeInt) ? AsmMnem.Fild : AsmMnem.Fld, new AsmLinkOperand(RegEsp));
        }

        void GenerateAssignment(SyntaxBinaryOp exp)
        {
            GenerateStatement(exp.Right);

            if (exp.Left.ExType is SymTypeFloat)
                GenerateFloatAssignment(exp);
            else
                GenerateIntAssignment(exp);

            if (exp.Left is SyntaxUnaryOp)
            {
                AddCmd(AsmMnem.Push, new AsmLinkOperand(VarName((exp.Left as SyntaxUnaryOp).Argument as SyntaxIdentefier)));
                AddCmd(AsmMnem.Pop, RegEax);
            }
            else
            {
                GetLink(exp.Left);
            }

            AddCmd(AsmMnem.Pop, new AsmLinkOperand(RegEax));
            AddCmd(AsmMnem.Push, new AsmLinkOperand(RegEax));
        }


        void GenerateIntAssignment(SyntaxBinaryOp exp)
        {
            if (exp.Operation == SubTokenType.Assignment)
                return;

            if (exp.ExType is SymTypeLink && exp.Right.ExType is SymTypeInt)
                GenerateConvIntToLink();

            GenerateStatement(exp.Left);

            AddCmd(AsmMnem.Pop, RegEax);
            AddCmd(AsmMnem.Pop, RegEbx);

            GenerateIntBinExpr(exp);
            AddCmd(AsmMnem.Push, RegEax);
        }


        void GenerateFloatAssignment(SyntaxBinaryOp exp)
        {
            AddCmd(
                (exp.Right.ExType is SymTypeInt) ? AsmMnem.Fild : AsmMnem.Fld,
                new AsmLinkOperand(RegEsp));

            if (exp.Operation != SubTokenType.Assignment)
            {
                GenerateStatement(exp.Left);
                AddCmd(AsmMnem.Fild, new AsmLinkOperand(RegEsp));

                GenerateFloatBinExpr(exp);
            }
            AddCmd(AsmMnem.Fstp, new AsmLinkOperand(RegEsp));
        }

        void GenerateCallFunc(SyntaxCallFunc exp)
        {
            if (exp.FunkName == "printf")
                GeneratePrintfFunc(exp);
            else
            {
                AddCmd(AsmMnem.Sub, RegEsp, AsmOpComSize);

                for (var it = exp.Args.Last; it != null; it = it.Previous)
                    GenerateStatement(it.Value);

                AddCmd(AsmMnem.Call, new AsmOperand("F$_" + exp.FunkName));

                if (exp.Args.Count != 0)
                    AddCmd(AsmMnem.Add, RegEsp, new AsmIntOperand(exp.Args.Count * CommandSize));
            }
        }

        void GeneratePrintfFunc(SyntaxCallFunc exp)
        {
            _globalData.AddLast(new AsmDb(Convert.ToString(_globalData.Count + 1), exp.Args.First.Value));

            GenerateStatement(exp.Args.Last.Value);
            if (exp.Args.Last.Value.ExType is SymTypeFloat)
            {
                AddCmd(AsmMnem.Fld, new AsmLinkOperand(RegEsp));
                AddCmd(AsmMnem.Sub, RegEsp, AsmOpComSize);
                AddCmd(AsmMnem.Fstp, new AsmOperand("qword ptr [esp]"));
            }

            AddCmd(AsmMnem.Push, new AsmOperand("offset S$_" + Convert.ToString(_globalData.Count) + ""));
            AddCmd(AsmMnem.Call, new AsmOperand("_imp__printf"));
            AddCmd(AsmMnem.Add, RegEsp, AsmOpComSize);

            if (exp.Args.Last.Value.ExType is SymTypeFloat)
                AddCmd(AsmMnem.Add, RegEsp, AsmOpComSize);
        }

        void GenerateConvIntToLink()
        {
            AddCmd(AsmMnem.Pop, RegEax);
            AddCmd(AsmMnem.Mov, RegEcx, new AsmIntOperand(4));
            AddCmd(AsmMnem.Mul, RegEcx);
            AddCmd(AsmMnem.Push, RegEax);
        }

        void GenerateBinExpr(SyntaxBinaryOp exp)
        {
            GenerateStatement(exp.Left);
            GenerateStatement(exp.Right);

            if (exp.ExType is SymTypeLink && exp.Right.ExType is SymTypeInt)
                GenerateConvIntToLink();

            if (exp.ExType is SymTypeFloat)
                GenerateFloatBinExpr(exp);
            else
            {
                AddCmd(AsmMnem.Pop, RegEbx);
                AddCmd(AsmMnem.Pop, RegEax);
                GenerateIntBinExpr(exp);
                AddCmd(AsmMnem.Push, RegEax);
            }
        }

        void GenerateFloatBinExpr(SyntaxBinaryOp exp)
        {
            LoadFloatVal(exp.Right);
            AddCmd(AsmMnem.Add, RegEsp, AsmOpComSize);

            LoadFloatVal(exp.Left);

            switch (exp.Operation)
            {
                case SubTokenType.Add:
                case SubTokenType.AddAssignment:
                    AddCmd(AsmMnem.Fadd, RegSt0, RegSt1);
                    break;
                case SubTokenType.Sub:
                case SubTokenType.SubAssignment:
                    AddCmd(AsmMnem.Fsub, RegSt0, RegSt1);
                    break;
                case SubTokenType.Multiply:
                case SubTokenType.MultiplyAssigment:
                    AddCmd(AsmMnem.Fmul, RegSt0, RegSt1);
                    break;
                case SubTokenType.Division:
                case SubTokenType.DivisionAssignment:
                    AddCmd(AsmMnem.Fdiv, RegSt0, RegSt1);
                    break;
            }

            AddCmd(AsmMnem.Fxch, RegSt1);
            AddCmd(AsmMnem.Fstp, new AsmLinkOperand(RegEsp));
            AddCmd(AsmMnem.Fstp, new AsmLinkOperand(RegEsp));
        }


        void GenerateIntBinExpr(SyntaxBinaryOp exp)
        {
            switch (exp.Operation)
            {
                case SubTokenType.Multiply:
                case SubTokenType.MultiplyAssigment:
                    AddCmd(AsmMnem.Mul, RegEbx);
                    break;
                case SubTokenType.Add:
                case SubTokenType.AddAssignment:
                    AddCmd(AsmMnem.Add, RegEax, RegEbx);
                    break;
                case SubTokenType.Sub:
                case SubTokenType.SubAssignment:
                    AddCmd(AsmMnem.Sub, RegEax, RegEbx);
                    break;
                case SubTokenType.SHL:
                    AddCmd(AsmMnem.Shl, RegEax, RegEbx);
                    break;
                case SubTokenType.SHR:
                    AddCmd(AsmMnem.Shr, RegEax, RegEbx);
                    break;
                case SubTokenType.DivisionAssignment:
                case SubTokenType.Division:
                    AddCmd(AsmMnem.Cdq, null);
                    AddCmd(AsmMnem.Idiv, RegEbx);
                    break;
                case SubTokenType.DivAssignment:
                case SubTokenType.Div:
                    AddCmd(AsmMnem.Cdq, null);
                    AddCmd(AsmMnem.Idiv, RegEbx);
                    AddCmd(AsmMnem.Mov, RegEdx, RegEax);
                    break;
                case SubTokenType.And:
                case SubTokenType.AndAssignment:
                    AddCmd(AsmMnem.And, RegEax, RegEbx);
                    break;
                case SubTokenType.Or:
                case SubTokenType.OrAssignment:
                    AddCmd(AsmMnem.Or, RegEax, RegEbx);
                    break;
                case SubTokenType.Low:
                case SubTokenType.Hi:
                case SubTokenType.LowOrEq:
                case SubTokenType.HiOrEq:
                case SubTokenType.Equal:
                case SubTokenType.NotEqual:

                    AddCmd(AsmMnem.Xor, RegEcx, RegEcx);
                    AddCmd(AsmMnem.Cmp, RegEax, RegEbx);

                    switch (exp.Operation)
                    {
                        case SubTokenType.Low:
                            AddCmd(AsmMnem.Setl, RegCl);
                            break;
                        case SubTokenType.Hi:
                            AddCmd(AsmMnem.Setg, RegCl);
                            break;
                        case SubTokenType.LowOrEq:
                            AddCmd(AsmMnem.Setle, RegCl);
                            break;
                        case SubTokenType.HiOrEq:
                            AddCmd(AsmMnem.Setge, RegCl);
                            break;
                        case SubTokenType.Equal:
                            AddCmd(AsmMnem.Sete, RegCl);
                            break;
                        case SubTokenType.NotEqual:
                            AddCmd(AsmMnem.Setne, RegCl);
                            break;
                    }

                    AddCmd(AsmMnem.Mov, RegEax, RegEcx);
                    break;
                case SubTokenType.LogicalAnd:
                    break;
                case SubTokenType.LogicalOr:
                    break;
            }
        }


        void GenerateLinkExp(SyntaxUnaryOp exp)
        {//если операция получения адреса то вызываеть операция что бы получить адрес, и дальше код не выполняеться
            switch (exp.Operation)
            {
                case SubTokenType.And:
                    GetLink(exp.Argument);
                    AddCmd(AsmMnem.Push, RegEax);
                    break;
                case SubTokenType.Multiply:
                    AddCmd(AsmMnem.Push, new AsmLinkOperand(VarName(exp.Argument as SyntaxIdentefier)));
                    AddCmd(AsmMnem.Pop, RegEax);
                    AddCmd(AsmMnem.Push, new AsmLinkOperand(RegEax));
                    break;
            }
        }

        void GenerateUnExpr(SyntaxUnaryOp exp)
        {
            if (exp.Operation == SubTokenType.And)
            {
                GetLink(exp.Argument);
                AddCmd(AsmMnem.Push, RegEax);
                return;
            }
            if (exp.Operation == SubTokenType.Multiply)
            {
                AddCmd(AsmMnem.Push, new AsmLinkOperand(VarName(exp.Argument as SyntaxIdentefier)));
                AddCmd(AsmMnem.Pop, RegEax);
                AddCmd(AsmMnem.Push, new AsmLinkOperand(RegEax));
                return;
            }

            GenerateStatement(exp.Argument);
            AddCmd(AsmMnem.Pop, RegEax);

            if (exp.Operation == SubTokenType.And)
                AddCmd(AsmMnem.Lea, RegEax, new AsmLinkOperand(RegEax));
            else if (exp.Argument.ExType is SymTypeInt)
            {
                switch (exp.Operation)
                {
                    case SubTokenType.Increment:
                        AddCmd(AsmMnem.Inc, RegEax);
                        break;
                    case SubTokenType.Decrement:
                        AddCmd(AsmMnem.Dec, RegEax);
                        break;
                    case SubTokenType.Sub:
                        AddCmd(AsmMnem.Neg, RegEax);
                        break;
                }
            }
            else if (exp.Argument.ExType is SymTypeLink)
            {
                switch (exp.Operation)
                {
                    case SubTokenType.Increment:
                        AddCmd(AsmMnem.Inc, RegEax);
                        break;
                    case SubTokenType.Decrement:
                        AddCmd(AsmMnem.Inc, RegEax);
                        break;
                }
            }

            AddCmd(AsmMnem.Push, RegEax);

            if (exp.Argument is SyntaxIdentefier)
            {
                AddCmd(AsmMnem.Pop, new AsmLinkOperand(VarName(exp.Argument as SyntaxIdentefier)));
                AddCmd(AsmMnem.Push, new AsmLinkOperand(VarName(exp.Argument as SyntaxIdentefier)));
            }
        }


        void GenerateReturn(SyntaxReturn exp)
        {
            GenerateStatement(exp.RetValue);
            AddCmd(AsmMnem.Pop, new AsmLinkOperand(RegEbp, 8 + _useFunk.Args * CommandSize));
            AddCmd(AsmMnem.Jmp, new AsmOperand("E$_" + _useFunk.Name));
        }


        #region Генерация операторов
        void GenerateAsmOperators(SyntaxOperators exp)
        {
            if (exp is SyntaxFor)
                GenerateAsmFor(exp as SyntaxFor);
            else if (exp is SyntaxWhile)
                GenerateAsmWhile(exp as SyntaxWhile);
            else if (exp is SyntaxDoWhile)
                GenerateAsmDo(exp as SyntaxDoWhile);
            else if (exp is SyntaxIf)
                GenerateAsmIf(exp as SyntaxIf);
        }

        void GenerateAsmCondition(SyntaxOperators exp, AsmLabel exit)
        {
            GenerateStatement(exp.Condition);

            AddCmd(AsmMnem.Pop, RegEax);
            AddCmd(AsmMnem.Test, RegEax, RegEax);
            AddCmd(AsmMnem.Jz, new AsmOperand(exit.Name));
        }

        void GenerateAsmFor(SyntaxFor exp)
        {
            var start = CreteLabel();
            var exit = CreteLabel();

            if (exp.Initialization != null)
            {
                GenerateStatement(exp.Initialization);
                AddCmd(AsmMnem.Add, RegEsp, AsmOpComSize);
            }

            AddLabel(start);

            if (exp.Condition != null)
                GenerateAsmCondition(exp, exit);

            GenerateAsmBlock(exp.Body);
            if (exp.Iteration != null)
            {
                GenerateStatement(exp.Iteration);
                AddCmd(AsmMnem.Add, RegEsp, AsmOpComSize);
            }

            AddCmd(AsmMnem.Jmp, new AsmOperand(start.Name));
            AddLabel(exit);
        }

        void GenerateAsmDo(SyntaxDoWhile exp)
        {
            var start = CreteLabel();
            var exit = CreteLabel();


            AddLabel(start);
            GenerateAsmBlock(exp.Body);
            if (exp.Condition != null)
                GenerateAsmCondition(exp, exit);
            AddCmd(AsmMnem.Jmp, new AsmOperand(start.Name));


            AddLabel(exit);
        }

        void GenerateAsmWhile(SyntaxWhile exp)
        {
            var start = CreteLabel();
            var exit = CreteLabel();


            AddLabel(start);


            if (exp.Condition != null)
                GenerateAsmCondition(exp, exit);
            GenerateAsmBlock(exp.Body);


            AddCmd(AsmMnem.Jmp, new AsmOperand(start.Name));
            AddLabel(exit);
        }

        void GenerateAsmIf(SyntaxIf exp)
        {
            var exit = CreteLabel();


            for (; exp != null; exp = exp.Next)
            {
                var next = CreteLabel();
                if (exp.Condition != null)
                    GenerateAsmCondition(exp, next);


                GenerateAsmBlock(exp.Body);
                AddCmd(AsmMnem.Jmp, new AsmOperand(exit.Name));
                AddLabel(next);
            }
            AddLabel(exit);
        }
        #endregion

        #endregion

        #region вывод


        String WriteData()
        {
            return _globalData.Aggregate(".data\n", (current, s) => current + (s.ToString() + "\n"));
        }

        String WriteCode()
        {
            String res = ".code\n";
            foreach (AsmFunck s in _functions)
                res += s.ToString();
            return res;
        }


        static String WriteHead()
        {
            return ".686\n" +
            ".model flat,stdcall\n"
            + "option casemap:none\n\n"
            + "include msvcrt.inc\n"
           + "includelib msvcrt.lib\n\n";

        }


        static String WriteFoot() { return "end F$_main"; }
        #endregion
        public override String ToString() { return WriteHead() + WriteData() + WriteCode() + WriteFoot(); }
    }
}